from queue import PriorityQueue
import random

import libtcodpy as libtcod

import world


class Game:
    def __init__(self, width, height, seed=None):
        self.width = width
        self.height = height
        self.tasks = PriorityQueue()
        self.time = 0
        self.stop = False
        if seed is None:
            self.rnd = random.Random()
        else:
            self.rnd = random.Random(seed)
        self.map = world.Map(width, height, self.rnd, self)

    def set_functions(self, get_next_key):
        self.get_next_key = get_next_key

    def add_task(self, time, function, parameters=[], relative=True):
        """Add a task to be done.

        time - time in steps when the function should run
        function - function that should run at given time
        parameters - additional parameters
        relative - whether time is relative or absolute
        """
        if relative:
            time += self.time + self.rnd.random()
            # TODO: Implement normal way of comparing same times.
        self.tasks.put((time, function, parameters))

    def restart_task(self, function, parameters=[]):
        """Add a task to be done immediately.

        function - function that should run at given time
        parameters - additional parameters
        """
        self.add_task(-1, function, parameters)

    def run_steps(self, number_of_steps):
        """Move forward in time and do tasks."""
        end_time = self.time + number_of_steps
        while self.time < end_time and not self.stop:
            task = self.tasks.get()
            if task[0] > end_time:
                self.time = end_time
                self.tasks.put(task)
            else:
                self.time, function, parameters = task
                function(*parameters)

    def run_tasks(self, number_of_tasks=1):
        """Move forward in time and do specific number of tasks."""
        for i in range(number_of_tasks) and not self.stop:
            task = self.tasks.get()
            self.time, function, parameters = task
            function(*parameters)

    def player_turn(self):
        """When it is player's turn check for keys that have nothing to do with
            player objects (such as quitting game).
        """
        layer = self.map.active_layer
        if layer.los_changed:
            layer.los_changed = False
            libtcod.map_compute_fov(layer.tcod_map, self.map.player.x,
                                    self.map.player.y, 0, True, libtcod.FOV_PERMISSIVE_3)

        key = self.get_next_key()
        if key.char == "escape":
            self.stop = True
        else:
            return key

    @property
    def player(self):
        return self.map.player
